﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.CMS.Membership;
using MethodWorx.Core.Validation;
using MethodWorx.CMS.Requests;
using MethodWorx.Core.DomainModel;
using MethodWorx.CMS.Providers;

namespace MethodWorx.CMS.Services
{
    public class UserService : IUserService
    {
        private IUserRepository users;
        private IPreviewSessionRepository previewSessions;
        private IUnitOfWorkFactory uowFactory;

        public UserService(ICmsDataProvider provider)
            : this( provider.GetService<IUserRepository>(),
                    provider.GetService<IPreviewSessionRepository>(),
                    provider.GetUnitOfWorkFactory())
        {
        }

        public UserService(IUserRepository users, IPreviewSessionRepository previewSessions, IUnitOfWorkFactory uowFactory)
        {
            if (users == null)
                throw new ArgumentNullException("users");

            if (previewSessions == null)
                throw new ArgumentNullException("previewSessions");

            this.users = users;
            this.previewSessions = previewSessions;
            this.uowFactory = uowFactory;
        }

        public PreviewSession CreatePreviewSession(Guid userId, string cmsUrl)
        {
            using (var uow = this.uowFactory.Begin())
            {
                var previewSession = this.previewSessions.CreateEntityInstance();
                previewSession.UserId = userId;
                previewSession.LastAccessed = DateTime.Now;
                previewSession.CmsUrl = cmsUrl;

                uow.RegisterAdded(previewSession);
                uow.Commit();

                return previewSession;
            }
        }

        public PreviewSession GetPreviewSession(Guid previewSessionId)
        {
            using (var uow = this.uowFactory.Begin())
            {
                var previewSession = this.previewSessions[previewSessionId];

                if (previewSession != null)
                {
                    previewSession.LastAccessed = DateTime.Now;
                    uow.RegisterChanged(previewSession);
                    uow.Commit();
                }

                return previewSession;
            }
        }

        public User Authenticate(string username, string password)
        {
            var user = this.users.FindByName(username);
            if (user != null && user.Username.ToLower() != "host")
            {
                //  verify the password
                if (user.Password != password)
                    user = null;
            }

            return user;
        }

        public void CreateUser(CreateUserRequest request, IValidationProvider validation, IUnitOfWorkFactory uowFactory)
        {
            try
            {
                if (validation.Validate(request) == true)
                {
                    if (request.Password != request.ConfirmPassword)
                        validation.AddError("ConfirmPassword", "Passwords do not match");

                    using (var uow = uowFactory.Begin())
                    {
                        request.User.UserType = UserType.SiteUser;
                        uow.RegisterAdded(request.User);
                        uow.Commit();
                    }
                }
            }
            catch (UniqueKeyViolationException ex)
            {
                //
                //  verify the uniqueness of the username
                if (ex.Properties.Contains("Username"))
                    validation.AddError("Username", "Username already exists");
                else
                    throw ex;
            }
        }

        public User GetUserById(Guid userId)
        {
            return this.users[userId];
        }

        public void SetDashboardWidgets(User user, Plugins.IPlugin foundPlugin, Plugins.DashboardWidget[] result)
        {
            using (var uow = this.uowFactory.Begin())
            {
                user.SetDashboardWidgets(foundPlugin, result);
                uow.RegisterChanged(user);
                uow.Commit();
            }
        }

        public void CreateDefaultUsers(IUnitOfWorkFactory uow)
        {
            this.CreateUser(new CreateUserRequest(this.users)
                                {
                                    Administrator   =  true,
                                    Username = "admin",
                                    Password = "admin",
                                    ConfirmPassword = "admin"
                                }, new NullValidationProvider(), uowFactory);
            this.CreateUser(new CreateUserRequest(this.users)
            {
                Administrator = true,
                Username = "host",
                Password = "host",
                ConfirmPassword = "host"
            }, new NullValidationProvider(), uowFactory);
        }

        public User CreateMember(string name, string emailAddress, string password, IValidationProvider provider)
        {
            try
            {
                using (var uow = uowFactory.Begin())
                {
                    var user = this.users.CreateEntityInstance();
                    user.Username = name;
                    user.EmailAddress = emailAddress;
                    user.Password = password;
                    user.UserType = UserType.SiteMember;
                    user.Active = true;

                    uow.RegisterAdded(user);
                    uow.Commit();

                    return user;
                }
            }
            catch (UniqueKeyViolationException ex)
            {
                //
                //  verify the uniqueness of the username
                if (ex.Properties.Contains("Username"))
                    provider.AddError("Username", "Username already exists");
                else
                    throw ex;
            }

            return null;
        }

        public IEnumerable<string> GetAttributes(UserType type)
        {
            return this.users.GetAttributes(type);
        }

        public bool AnyExists()
        {
            return (this.users.All().Count() > 0);
        }


        public User GetUserByName(string name)
        {
            return this.users.FindByName(name);
        }
    }
}
